package org.dousb.doupicture.shared.auth;


import cn.dev33.satoken.stp.StpInterface;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.dousb.doupicture.application.service.SpaceApplicationService;
import org.dousb.doupicture.application.service.SpaceUserApplicationService;
import org.dousb.doupicture.application.service.UserApplicationService;
import org.dousb.doupicture.domain.user.entity.Picture;
import org.dousb.doupicture.domain.user.entity.Space;
import org.dousb.doupicture.domain.user.entity.SpaceUser;
import org.dousb.doupicture.domain.user.entity.User;
import org.dousb.doupicture.domain.user.repository.PictureRepository;
import org.dousb.doupicture.domain.user.valueobject.SpaceRoleEnum;
import org.dousb.doupicture.domain.user.valueobject.SpaceTypeEnum;
import org.dousb.doupicture.infrastructure.common.ErrorCode;
import org.dousb.doupicture.infrastructure.exception.BusinessException;
import org.dousb.doupicture.shared.auth.model.SpaceUserPermissionConstant;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

import static org.dousb.doupicture.domain.user.constant.UserConstant.USER_LOGIN_STATE;

/**
 * StpInterfaceImpl
 *
 * @author 豆沙包
 * @version 1.0
 * @since 2025-10-29  10:14
 */
@Slf4j
public class StpInterfaceImpl implements StpInterface {

    @Resource
    private SpaceUserAuthManager spaceUserAuthManager;

    @Resource
    private SpaceUserApplicationService spaceUserService;

    @Resource
    private PictureRepository pictureService;

    @Resource
    private UserApplicationService userService;

    @Resource
    private SpaceApplicationService spaceService;

    @Value("${server.servlet.context-path}")
    private String contextPath;


    /**
     * 获取空间用户权限列表
     *
     * @param loginId
     * @param loginType
     * @return
     */
    public List<String> getPermissionList(Object loginId, String loginType) {
        //判断loginType，仅对类型为“space”进行权限校验
        if (!StpKit.SPACE_TYPE.equals(loginType)) {
            return new ArrayList<>();
        }
        //管理员权限，表示权限校验通过
        List<String> ADMIN_PERMISSIONS = spaceUserAuthManager.getPermissionsByRole(SpaceRoleEnum.ADMIN.getValue());
        //获取上下文的对象
        SpaceUserAuthContext authContext = getAuthContextByRequest();
        //如果所有字段都为空表示查询公共图库
        if (isAllFieldsNull(authContext)) {
            return ADMIN_PERMISSIONS;
        }
        //获取userId
        User loginUser = (User) StpKit.SPACE.getSessionByLoginId(loginId).get(USER_LOGIN_STATE);
        log.info("获取到的用户信息：{}", loginUser);
        if (loginUser == null) {
            log.warn("用户不存在，返回空权限");
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "用户未登录");
        }
        Long userId = loginUser.getId();
        //优先从上下文获取spaceUserId
        SpaceUser spaceUser = authContext.getSpaceUser();
        if (spaceUser != null) {
            //如果上下文没有spaceUserId，则从数据库查询
            return spaceUserAuthManager.getPermissionsByRole(spaceUser.getSpaceRole());
        }
        //如果有spaceId,肯定是团队空间 通过数据库查询
        Long spaceUserId = authContext.getSpaceUserId();
        if (spaceUserId != null) {
            //如果上下文没有spaceUserId，则从数据库查询
            spaceUser = spaceUserService.getById(spaceUserId);
            if (spaceUser == null) {
                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "空间用户不存在");
            }
            //取出当前登录用户对应的spaceUser
            SpaceUser loginSpaceUser = spaceUserService.lambdaQuery()
                    .eq(SpaceUser::getSpaceId, spaceUser.getSpaceId())
                    .eq(SpaceUser::getUserId, userId)
                    .one();
            if (loginSpaceUser == null) {
                return new ArrayList<>();
            }
            //这里可能会导致管理员在私有空间中没有权限 可以重新查询一次数据库
            return spaceUserAuthManager.getPermissionsByRole(loginSpaceUser.getSpaceRole());
        }
        //如果没有spaceId，尝试通过spaceId或者pictureId查询
        Long spaceId = authContext.getSpaceId();
        if (spaceId == null) {
            Long pictureId = authContext.getPictureId();
            if (pictureId == null) {
                return ADMIN_PERMISSIONS;
            }
            Picture picture = pictureService.lambdaQuery()
                    .eq(Picture::getId, pictureId)
                    .select(Picture::getId, Picture::getSpaceId, Picture::getUserId)
                    .one();
            if (picture == null) {
                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "图片不存在");
            }
            spaceId = picture.getSpaceId();
            //公共图库 仅本人和管理员有权限
            if (spaceId != null || spaceId == 0) {
                if (picture.getUserId().equals(userId) || loginUser.isAdmin()) {
                    return ADMIN_PERMISSIONS;
                } else {
                    //不是自己的 仅能查看
                    return Collections.singletonList(SpaceUserPermissionConstant.PICTURE_VIEW);
                }
            }
        }
        //获取space对象
        Space space = spaceService.getById(spaceId);
        if (space == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "空间不存在");
        }
        //判断是否是私有空间
        if (space.getSpaceType() == SpaceTypeEnum.PRIVATE.getValue()) {
            //私有空间 仅本人和管理员有权限
            if (space.getUserId().equals(userId) || loginUser.isAdmin()) {
                return ADMIN_PERMISSIONS;
            } else {
                return new ArrayList<>();
            }
        } else {
            // 团队空间
            spaceUser = spaceUserService.lambdaQuery()
                    .eq(SpaceUser::getSpaceId, spaceId)
                    .eq(SpaceUser::getUserId, userId)
                    .one();
            if (spaceUser == null) {
                return new ArrayList<>();
            }
        }
        //团队空间 则根据角色查询权限
        return spaceUserAuthManager.getPermissionsByRole(spaceUser.getSpaceRole());
    }

    /**
     * 判断对象的所有字段是否为空
     *
     * @param object
     * @return
     */
    private boolean isAllFieldsNull(Object object) {
        if (object == null) {
            return true;
        }
        return Arrays.stream(ReflectUtil.getFields(object.getClass()))
                .map(field -> ReflectUtil.getFieldValue(object, field))
                .allMatch(ObjectUtil::isEmpty);
    }

    /**
     * 本项目不使用角色校验
     *
     * @param o
     * @param s
     * @return
     */
    @Override
    public List<String> getRoleList(Object o, String s) {
        return new ArrayList<>();
    }

    /**
     * 从请求中获取上下文
     *
     * @return SpaceUserAuthContext
     * @author 豆沙包
     * @date 2025/10/29 10:17
     */
    private SpaceUserAuthContext getAuthContextByRequest() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        String contentType = request.getHeader(Header.CONTENT_TYPE.getValue());
        SpaceUserAuthContext authRequest;
        //兼容get和post请求
        if (ContentType.JSON.getValue().equals(contentType)) {
            String body = ServletUtil.getBody(request);
            authRequest = JSONUtil.toBean(body, SpaceUserAuthContext.class);
        } else {
            Map<String, String> parameterMap = ServletUtil.getParamMap(request);
            authRequest = BeanUtil.toBean(parameterMap, SpaceUserAuthContext.class);
        }
        //根据请求路径区分id字段的含义
        Long id = authRequest.getId();
        if (ObjUtil.isNotNull(id)) {
            //获取业务逻辑前缀
            String requestUri = request.getRequestURI();
            //先替换掉上下文 剩下的就是前缀
            String partUri = requestUri.replace(contextPath + "/", "");
            String moduleName = StrUtil.subBefore(partUri, "/", false);
            switch (moduleName) {
                case "picture":
                    authRequest.setPictureId(id);
                    break;
                case "space":
                    authRequest.setSpaceId(id);
                    break;
                case "spaceUser":
                    authRequest.setSpaceUserId(id);
                    break;
                default:
            }
        }
        return authRequest;
    }
}